#set( $symbol_pound = '#' )
#set( $symbol_dollar = '$' )
#set( $symbol_escape = '\' )
package ${package}.utils;

import cn.hutool.core.convert.Convert;
import cn.hutool.core.util.ArrayUtil;
import com.alibaba.fastjson.JSON;
import com.google.common.collect.Lists;
import lombok.extern.slf4j.Slf4j;
import ${package}.stream.Seq;
import org.springframework.data.util.Pair;
import org.springframework.util.ReflectionUtils;

import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.invoke.SerializedLambda;
import java.lang.reflect.Method;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

@Slf4j
public class MethodHandleUtils {

    private static Pattern PATTERN = Pattern.compile("L(.*);");
    public static <T, R> R invokeMethod(T target, SerializableFunction<T, R> function, Object... args) throws Throwable {
        SerializedLambda serializedLambda = getSerializedLambda(function);
        String methodName = serializedLambda.getImplMethodName();
        String implMethodSignature = serializedLambda.getImplMethodSignature();
        Matcher matcher = PATTERN.matcher(implMethodSignature);
        Class<?> ryClass = null;
        Class<?> paramClass = null;
        int count = 0;
        while (matcher.find()) {
            String className = matcher.group(1);
            className = className.replaceAll("/", ".");
           if (count == 0) {
               ryClass = Class.forName(className);
           } else if (count == 1) {
               paramClass = Class.forName(className);
           }
           count++;
        }
        MethodType methodType = Objects.isNull(paramClass) ? MethodType.methodType(ryClass) : MethodType.methodType(ryClass, paramClass);
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle virtual = lookup.findVirtual(target.getClass(), methodName, methodType);
        Object obj = virtual.invoke(args);
        return Objects.isNull(obj) ? null : (R) obj;
    }

    /**
     * 通过反射调用方法
     * @param target
     * @param methodName
     * @param rtClass
     * @param paramClass
     * @return
     * @param <T>
     * @param <R>
     * @throws Throwable
     */
    public static <T,R> R invokeMethod(T target, String methodName, Class<R> rtClass, Pair<Class<?>, ?> ... paramClass) throws Throwable{
        MethodType methodType = MethodType.methodType(rtClass);
        Class<?>[] paramTypeArr = new Class[paramClass.length];
        Object[] argArr = new Object[paramClass.length];
        if(ArrayUtil.isNotEmpty(paramClass)){
            for (int i = 0; i < paramClass.length; i++) {
                paramTypeArr[i] = paramClass[i].getFirst();
                argArr[i] = paramClass[i].getSecond();
            }
            methodType = MethodType.methodType(rtClass, paramTypeArr);
        }
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle virtual = lookup.findVirtual(target.getClass(), methodName, methodType);
        Object obj = virtual.invoke(argArr);
        return Objects.isNull(obj) ? null : Convert.convert(rtClass, obj);
    }
    /**
     * 通过反射调用方法
     * @param target
     * @param method
     * @return
     * @throws Throwable
     */
    public static Object invokeMethod(Object target, Method method, Object... args) throws Throwable{
        MethodHandles.Lookup lookup = MethodHandles.lookup();
        MethodHandle virtual = lookup.findVirtual(target.getClass(), method.getName(), MethodType.methodType(method.getReturnType(), method.getParameterTypes()));
        Object obj = virtual.invoke(args);
        return Objects.isNull(obj) ? null : obj;
    }
    public static <T, R> SerializedLambda getSerializedLambda(SerializableFunction<T, R> function) {
        Method writeReplace = ReflectionUtils.findMethod(function.getClass(), "writeReplace");
        ReflectionUtils.makeAccessible(writeReplace);
        SerializedLambda serializedLambda = (SerializedLambda) ReflectionUtils.invokeMethod(writeReplace, function);
        return serializedLambda;
    }

    public void test() throws Throwable {

        System.out.println("1");
    }
    public static void main(String[] args) throws Throwable {
//        XftRequest xftRequest = new XftRequest();
//        xftRequest.setRequestType("POST");
//        String s = invokeMethod(xftRequest, XftRequest::getRequestType);
//        System.out.println(s);
//        Method method = ReflectionUtils.findMethod(MethodHandleUtils.class, "test");
//        MethodHandleUtils methodHandleUtils = new MethodHandleUtils();
//        Object o = invokeMethod(methodHandleUtils, method);
//        Class<?> returnType = method.getReturnType();
//        Class.forName(returnType.getName());
//        Class<?>[] parameterTypes = method.getParameterTypes();
//        System.out.println("1");
        List<Integer> list = Lists.newArrayList(1, 2, 3, 4, 5);
        Seq.of(list)
                .partition(3)
                .map(Seq::toList)
                .foreach(list1 -> log.info("listList={}", JSON.toJSONString(list1)));
    }
}
